<template>
  <div id="global-uploader">
    <uploader
      ref="uploader"
      :options="options"
      :autoStart="false"
      :file-status-text="fileStatusText"
      @file-added="onFileAdded"
      @file-success="onFileSuccess"
      @file-progress="onFileProgress"
      @file-error="onFileError"
      class="uploader-app"
      >
      <uploader-unsupport />
      <uploader-btn id="global-uploader-btn" :single="true" :attrs="attrs" ref="uploadBtn">选择文件<i class="el-icon-upload el-icon--right"></i></uploader-btn>
      <uploader-list v-show="panelShow">
        <div class="file-panel" slot-scope="props">
          <div class="file-title" :style="{ backgroundColor: theme }">
            <strong>文件列表</strong>
            <div class="operate fr">
<!--              <el-button @click="fileListShow" type="text" :title="collapse ? '打开':'隐藏' ">-->
<!--                <i class="icon" :class="collapse ? 'el-icon-caret-top' : 'el-icon-caret-bottom'"></i>-->
<!--              </el-button>-->
              <el-button @click="fileListShow" type="text" title="关闭">
                <i class="el-icon-close" style="color:#fff;font-size: 18px"></i>
              </el-button>
            </div>
          </div>
          <ul class="file-list">
            <li v-for="file in props.fileList" :key="file.id">
              <uploader-file :class="'file_' + file.id" ref="files" :file="file" :list="true"></uploader-file>
            </li>
            <div class="no-file" v-if="!props.fileList.length"><i class="iconfont icon-empty-file"></i> 暂无待上传文件</div>
          </ul>
        </div>
      </uploader-list>
    </uploader>
  </div>
</template>

<script>
import BUS from '@/utils/bus'
import $ from 'jquery'
import SparkMD5 from 'spark-md5'
import { getAuthenticationHeader } from '@/utils/store'
import { mergeChunkFile } from '@/api/tool/file'

export default {
  data() {
    return {
      attrs: {
        accept: ['.png', '.jpg', '.jpeg', '.gif', '.bmp', '.mp4', '.rmvb', '.mkv', '.wmv', '.flv', '.doc', '.docx', '.xls', '.xlsx', '.ppt', '.pptx', '.pdf', '.txt', '.tif', '.tiff','.rar','.zip']
      },
      options: {
        headers: getAuthenticationHeader(),
        // 目标上传 URL，默认POST
        target: this.$store.state.app.server_api + `${process.env.VUE_APP_MODULE_ADMIN_API}/api/file/chunk/upload`,
        // 分块大小(单位：字节)
        chunkSize: '51200000',
        // 上传文件时文件内容的参数名
        fileParameterName: 'file',
        // 失败后最多自动重试上传次数
        maxChunkRetries: 3,
        query: {
          path: this.path
        },
        // 是否开启服务器分片校验，对应GET类型同名的target URL
        testChunks: true,
        // 服务器分片校验函数
        checkChunkUploadedByResponse: function (chunk, response) {
          let objMessage = JSON.parse(response)
          if (objMessage.skipUpload) {
            return true
          }
          return (objMessage.uploadedChunks || []).indexOf(chunk.offset + 1) >= 0
        }
      },
      panelShow: false,
      collapse: false,
      fileStatusText: {
        success: '上传成功',
        error: '上传失败',
        uploading: '上传中',
        paused: '暂停',
        waiting: '等待上传'
      }
    }
  },
  mounted() {
    BUS.$on('openUploader', query => {
      if (query.authorization) {
        this.options.headers = query.authorization
        delete query.authorization
      }

      this.options.target = this.$store.state.app.server_api + `${process.env.VUE_APP_MODULE_ADMIN_API}/api/file/chunk/upload`
      if (query.target) {
        this.options.target = this.$store.state.app.server_api + query.target
        delete query.target
      }

      this.attrs.accept = ['.png', '.jpg', '.jpeg', '.gif', '.bmp', '.mp4', '.rmvb', '.mkv', '.wmv', '.flv', '.doc', '.docx', '.xls', '.xlsx', '.ppt', '.pptx', '.pdf', '.txt', '.tif', '.tiff','.rar','.zip']
      if (query.accept) {
        this.attrs.accept = query.accept
        delete query.accept
      }

      this.options.query = query || {}

      if (this.$refs.uploadBtn) {
        $('#global-uploader-btn').click()
      }
    })

    BUS.$on('showUploader', query => {
      this.options.query = query || {}
      this.options.headers =query.authorization
      this.options.target = query.target ? this.$store.state.app.server_api + query.target
        : this.$store.state.app.server_api + `${process.env.VUE_APP_MODULE_ADMIN_API}/api/file/chunk/upload`
      this.collapse = true
      this.panelShow = true
      setTimeout(() => {
        let $list = $('#global-uploader .file-list')
        $list.slideDown()
        this.collapse = false
      }, 100)
    })
  },
  destroyed() {
    BUS.$off('openUploader');
  },
  computed: {
    uploader() {
      return this.$refs.uploader.uploader
    },
    theme() {
      return this.$store.state.settings.theme
    }
  },
  methods: {
    onFileAdded(file) {
      this.collapse = false
      this.panelShow = true
      this.computeMD5(file)
    },
    onFileProgress(rootFile, file, chunk) {
      // console.log(`上传中 ${file.name}，chunk：${chunk.startByte / 1024 / 1024} ~ ${chunk.endByte / 1024 / 1024}`)
    },
    // rootFile:成功上传的文件  file:当前成功的 Uploader.File  response:服务端响应内容  chunk:Uploader.Chunk 实例
    onFileSuccess(rootFile, file, response, chunk) {
      mergeChunkFile(rootFile).then( response => {
        if(response.code === 200) {
          BUS.$emit('fileSuccess', response)
          $('.uploader-file[status=success] .uploader-file-remove').css('display', 'block')
        } else {
          BUS.$emit('fileError', response)
        }
      }).catch(function (error) {
        BUS.$emit('fileError', error)
      })
    },
    onFileError(rootFile, file, response, chunk) {
      this.$modal.msgError(response)
    },
    computeMD5(file) {
      this.statusSet(file.id, 'md5')
      file.pause()
      // 单个文件的大小限制2G
      let fileSizeLimit = 2 * 1024 * 1024 * 1024
      if (file.size > fileSizeLimit) {
        this.$modal.msgError('文件大小不能超过2G')
        file.cancel()
      }

      let fileReader = new FileReader()
      let time = new Date().getTime()
      let blobSlice = File.prototype.slice || File.prototype.mozSlice || File.prototype.webkitSlice
      let currentChunk = 0
      // 每段chunk大小为50MB
      const chunkSize = 50 * 1024 * 1000
      let chunks = Math.ceil(file.size / chunkSize)
      let spark = new SparkMD5.ArrayBuffer()

      loadNext()

      fileReader.onload = (e => {
        spark.append(e.target.result)
        // 利用文件首尾分片的md5拼接作为整个文件md5
        while (currentChunk < chunks.value) {
          if (currentChunk == 1 || (currentChunk == chunks.value - 1)) {
            loadNext()
          } else {
            currentChunk++
          }
        }

        let md5 = spark.end()
        file.uniqueIdentifier = md5
        file.resume()
        this.statusRemove(file.id)
        // console.log(`MD5计算完毕：${file.name} \nMD5：${md5} \n分片：${chunks} 大小:${file.size} 用时：${new Date().getTime() - time} ms`)
      })

      fileReader.onerror = function () {
        this.$modal.msgError(`文件${file.name}读取出错，请检查该文件`)
        file.cancel()
      }

      function loadNext() {
        let start = currentChunk * chunkSize
        let end = ((start + chunkSize) >= file.size) ? file.size : start + chunkSize

        fileReader.readAsArrayBuffer(blobSlice.call(file.file, start, end))
        currentChunk++
      }
    },
    statusSet(id, status) {
      let statusMap = {
        md5: {
          text: '校验MD5',
          bgc: '#fff'
        },
        merging: {
          text: '合并中',
          bgc: '#e2eeff'
        },
        transcoding: {
          text: '转码中',
          bgc: '#e2eeff'
        },
        failed: {
          text: '上传失败',
          bgc: '#e2eeff'
        }
      }

      this.$nextTick(() => {
        $(`<p class="myStatus_${id}"></p>`).appendTo(`.file_${id} .uploader-file-status`).css({
          'position': 'absolute',
          'top': '0',
          'left': '0',
          'right': '0',
          'bottom': '0',
          'zIndex': '1',
          'line-height': 'initial',
          'backgroundColor': statusMap[status].bgc
        }).text(statusMap[status].text)
      })
    },
    statusRemove(id) {
      this.$nextTick(() => {
        $(`.myStatus_${id}`).remove()
      })
    },
    fileListShow() {
      let $list = $('#global-uploader .file-list')

      if ($list.is(':visible')) {
        $list.slideUp()
        this.collapse = true
        setTimeout(() => {
          this.collapse = false
          this.panelShow = false
        }, 400)
      } else {
        $list.slideDown()
        this.collapse = false
      }
    },
    close() {
      this.uploader.cancel()
      this.panelShow = false
    },
    error(msg) {
      this.$modal.notifyError(msg)
    }
  }
}
</script>

<style scoped lang="scss">
  #global-uploader {
    position: fixed;
    z-index: 20;
    right: 15px;
    bottom: 15px;

    h1, h2 {
      font-weight: normal;
    }

    ul {
      list-style-type: none;
      padding: 0px 4px;
    }

    li {
      display: inline-block;
    }

    .uploader-app {
      width: 660px;
    }

    .file-panel {
      background-color: #fff;
      border: 1px solid #e2e2e2;
      border-radius: 7px 7px 0 0;
      box-shadow: 0 0 10px rgba(0, 0, 0, .2);

      .file-title {
        display: flex;
        padding: 0 15px;
        line-height: 40px;
        border-bottom: 1px solid #ddd;
        color: #E7ECF2;

        .operate {
          position: absolute;
          right: 15px;
        }
      }

      .file-list {
        position: relative;
        height: 264px;
        width: 654px;
        overflow-x: hidden;
        overflow-y: auto;
        background-color: #fff;

        > li {
          background-color: #fff;
        }
      }

      &.collapse {
        .file-title {
          background-color: #E7ECF2;
        }
      }
    }

    .no-file {
      position: absolute;
      top: 50%;
      left: 50%;
      transform: translate(-50%, -50%);
      font-size: 16px;
    }

    .uploader-file {
      width: 654px;
    }

    ::v-deep .uploader-file-icon {
      &:before {
        content: '' !important;
      }

      &[icon=image] {
        background: url(../../assets/images/upload/image-icon.png);
      }

      &[icon=video] {
        background: url(../../assets/images/upload/video-icon.png);
      }

      &[icon=document] {
        background: url(../../assets/images/upload/text-icon.png);
      }
    }

    //::v-deep .uploader-file-actions > span {
    //  margin-right: 6px;
    //}
  }

  /* 隐藏上传按钮 */
  #global-uploader-btn {
    position: absolute;
    clip: rect(0, 0, 0, 0);
  }
</style>
